# -*- coding:utf-8 -*-
"""
    Fundation daily valuation crawler
    @Author:ibunny
    @Date: 2021.02.26
"""


from __future__ import division, print_function
import requests
import json
import os
import time
import re
import random
import threading
import datetime
import pandas as pd
from .fund_object import FundationObj
import smtplib
from email.mime.text import MIMEText
from email.header import Header
import locale


class FundCrawler(object):
    """
        Query info for fundation from http://fund.eastmoney.com/ 
    """
    def __init__(self, fund_code_input_file):
        self._fund_code_list = []
        self._fund_obj_dir = dict()
        self._fund_code_input_file = fund_code_input_file
        self._user_fund_input_file = './data/my_fund.csv'
        self._fund_code_output_file = './data/fund_info.csv'
        self._mail_conf_file = './data/mail_config.json'
        self._fail_fund_code_list = []
        self._user_agent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.192 Safari/537.36"
        self._MAX_RETRY_COUNT = 10
        self._OK_STATUS = 200
        if not os.path.isfile(self._fund_code_input_file):
            raise ValueError("[error]: {file_path} is not a file, please check the path!".format(file_path=self._fund_code_input_file))
        if not os.path.isfile(self._mail_conf_file):
            raise ValueError("[error]: make sure data/mail_config.json exists and is formated as in README.md !")

        with open(self._mail_conf_file, 'r') as f:
            self.mail_config_json = json.load(f)
        self.parse_fund_code_in_file(self._fund_code_input_file)


    def parse_fund_code_in_file(self, input_file):
        # read fund code from _fund_code_input_file
        with open(input_file, 'r') as fi:
            for line in fi.readlines():
                fund_code = line.strip()                    
                if fund_code not in self._fund_code_list:
                    self._fund_code_list.append(fund_code)
        
        if len(self._fund_code_list) == 0:
            raise ValueError("[error]: Please check file {file_path}, fund code is empty".format(file_path=self._fund_code_input_file))
        
            

    def save_funds_info(self):
        # save fund info to out_file_path
        self.df = pd.DataFrame([fund_obj.obj2dict(self.mail_config_json['cal_user_gain_rate']) for fund_obj in list(self._fund_obj_dir.values())])
        # _, encoding = locale.getdefaultlocale()
        self.df.to_csv(self._fund_code_output_file, encoding='gbk', index=False)


    def send_fund_info_to_mail(self):
        # ref: https://www.runoob.com/python/python-email.html

        if self.mail_config_json['cal_user_gain_rate']:
            rate_col = '基金持有收益率估值'
        else:
            rate_col = '基金当日涨跌率'
        df_filter = self.df[(self.df[rate_col]<self.mail_config_json['rise_rate_floor']) | (self.df[rate_col]>self.mail_config_json['rise_rate_ceil'])]
        if len(df_filter) > 0:
            message = MIMEText(df_filter.to_html(), 'html', 'utf-8')
        elif len(self.mail_config_json['safe_message']) > 0:
            message = MIMEText(self.mail_config_json['safe_message'], 'plain', 'utf-8')
        else:
            return
        message['From'] = Header(self.mail_config_json['from'], 'utf-8')
        message['To'] =  Header(self.mail_config_json['to'], 'utf-8')
        message['Subject'] = Header(self.mail_config_json['subject'], 'utf-8')
        
        smtpObj = smtplib.SMTP() 
        smtpObj.connect(self.mail_config_json['mail_host'], 25)    
        smtpObj.login(self.mail_config_json['send_mail_addr'], self.mail_config_json['grant_code'])  
        smtpObj.sendmail(self.mail_config_json['send_mail_addr'], self.mail_config_json['receive_mail_addr'], message.as_string())
        smtpObj.quit()


    def query_funds_info(self, cal_latest_fund_data = False):
        # [http://fundgz.1234567.com.cn/js/320007.js?rt=1614358093826]
        current_unix_time = float(time.mktime(datetime.datetime.now().timetuple()))
        fund_valuation_url_list = ["http://fundgz.1234567.com.cn/js/{fund}.js?rt={rt}".format(fund=fund_code, rt=current_unix_time) for fund_code in self._fund_code_list]
        for fund_url in fund_valuation_url_list:
            fund_code = re.findall("\d+", fund_url)[1]
            fund_obj= self.query_fund_url_and_parse(fund_url)
            self._fund_obj_dir[fund_code] = fund_obj

        # add user hold info
        if self.mail_config_json['cal_user_gain_rate']:
            if not os.path.isfile(self._user_fund_input_file):
                raise ValueError("[error]: make sure {file} exists and is formated as in README.md !".format(file=self._user_fund_input_file))

            with open(self._user_fund_input_file, 'r+') as f:
                lines = f.readlines()
                f.seek(0)         
                f.truncate() 
                for line in lines:
                    user_fund_data = line.strip().split(',')
                    if len(user_fund_data) < 3:
                        raise ValueError("[error]: {fund_code_input_file} must have at least 3 columns [fund_code, fund_hold_price, func_hold_num] ".format(fund_code_input_file=self._fund_code_input_file))
                    fund_code, fund_hold_price, fund_hold_num = user_fund_data[0], user_fund_data[1], user_fund_data[2]
                    fund_obj = self._fund_obj_dir[fund_code]
                    fund_name = fund_obj.get_fund_name()

                    # update fund name to my_fund.csv
                    f.write(",".join([fund_code, fund_hold_price, fund_hold_num])+","+fund_name+"\n")

                    # calculate indices
                    fund_obj.set_user_fund_hold_price(float(fund_hold_price))
                    fund_obj.set_user_fund_hold_num(float(fund_hold_num))
                    fund_obj.cal_user_estimate_gain_rate()
                    fund_obj.cal_user_recmd_cover_money(self.mail_config_json['target_return_rate_floor'])
        
        if len(self._fail_fund_code_list) != 0:
            raise ValueError("[error]: Please check fund code {fail_funds}, query info fail!".format(fail_funds=self._fail_fund_code_list))


    def parse_valuation_html(self, html_text):
        """
            jsonpgz({"fundcode":"320007","name":"zhanan","jzrq":"2021-02-25","dwjz":"1.7440","gsz":"1.7196","gszzl":"-1.40","gztime":"2021-02-26 15:00"});
        """
        html_json = json.loads(re.findall(r"\{.*\}", html_text)[0])
        fund_code = html_json["fundcode"]
        fund_name = html_json["name"]
        fund_real_valuation = float(html_json["dwjz"])
        fund_current_valuation = float(html_json["gsz"])
        current_rise_rate = float(html_json["gszzl"])
        current_valuation_time = html_json["gztime"]
        fund_obj = FundationObj(fund_code, fund_name, fund_real_valuation, fund_current_valuation, current_rise_rate, current_valuation_time)
        return fund_obj


    def query_fund_url_and_parse(self, fund_url):
        req = requests.get(fund_url, verify=False)
        retry = 0
        while req.status_code != self._OK_STATUS and retry < self._MAX_RETRY_COUNT:
            fund_code = re.findall(r"\d+", fund_url)[1]
            print(str(req.status_code) + ' retry query fund  '+ fund_code)
            req = requests.get(fund_url, verify=False)
            time.sleep(random.randint(1, 3))
            retry += 1
        if self._MAX_RETRY_COUNT == retry:
            self._fail_fund_code_list.append(fund_code)
            return None
        
        fund_obj = self.parse_valuation_html(req.text)
        return fund_obj
    

